package com.hccake.ballcat.codegen.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hccake.ballcat.codegen.constant.MailConstants;
import com.hccake.ballcat.codegen.model.dto.ExecSqlDTO;
import com.hccake.ballcat.codegen.model.dto.MailInfoDTO;
import com.hccake.ballcat.codegen.model.vo.ExecSqlVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.mail.search.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/***
 * 1、有一行 包含 通过这两个字，确定是，审批通过的【不能超过20个子】 2、行以http://git.murongtech.com:8186开头，如果后面又sql
 * ,sql文件，如果不是以.sql结尾 文件夹 【包好_RB不处理】 ， 如果是包含BK 优先处理 3、dsName 通过标题 | 分割，第五个 4、如果标题中包含 自动|脚本
 * ， 这类邮箱，我才处理，可以吧
 *
 * "cheng_cd@murongtech.com", "evgiqDLMDHMX9Mhq"
 */
@Slf4j
@Component
public final class MailUtil implements InitializingBean {

	@Autowired
	private RedisTemplate redisTemplate;

	@Value("${mail.sqlRowMatch}")
	private String mailSqlRowMatch;

	private Session session = null;

	private Session sessionWrite = null;

	@Value("${mail.userName}")
	private String mailUserName;

	@Value("${mail.password}")
	private String mailPassword;

	@Value("${mail.onlyCMail}")
	private String onlyCMail;

	@Override
	public void afterPropertiesSet() {
		getSession();
		getWrSession();
	}

	private Session getSession() {
		if (session == null) {
			Properties props = new Properties();
			props.setProperty("mail.store.protocol", "imaps");
			props.setProperty("mail.imap.host", "imap.exmail.qq.com");
			props.setProperty("mail.imap.port", "993");
			props.setProperty("mail.imap.ssl.enable", "true");
			// 显式指定 SSL 套接字工厂
			props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			session = Session.getInstance(props);
		}
		return session;
	}

	private void getWrSession() {
		if (sessionWrite == null) {
			// 配置邮件会话属性
			Properties properties = new Properties();
			properties.put("mail.smtp.host", "smtp.exmail.qq.com");
			properties.put("mail.smtp.port", "465");
			properties.put("mail.smtp.auth", "true");
			properties.put("mail.smtp.ssl.enable", "true");

			// 创建邮件会话
			sessionWrite = Session.getInstance(properties, new javax.mail.Authenticator() {
				protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
					return new javax.mail.PasswordAuthentication(mailUserName, mailPassword);
				}
			});
		}
	}

	public List<MailInfoDTO> readEmailPop() throws Exception {
		if (null == session) {
			getSession();
		}
		long start = System.currentTimeMillis();
		log.info("邮箱登录：" + new Date());

		// 使用 IMAP 协议
		Store store = session.getStore("imaps");
		store.connect("imap.exmail.qq.com", mailUserName, mailPassword);
		log.info("邮箱登录成功：" + new Date());

		// 选择文件夹
		Folder folder = store.getFolder("INBOX");
		List<Message> messages = searchEmailsLastHourWithIMAP(folder);

		log.info("搜索邮件结束：" + new Date() + ", 耗时：" + (System.currentTimeMillis() - start));
		log.info("读取的邮件总数: " + messages.size() + "---" + mailUserName + "---" + mailPassword);

		List<MailInfoDTO> rs = new ArrayList<>();
		if (!messages.isEmpty()) {
			rs = parseFileMessage(messages); // 处理邮件
		}
		return rs;
	}

	public List<Message> searchEmailsLastHourWithIMAP(Folder folder) throws Exception {
		// 打开文件夹
		if (!folder.isOpen()) {
			folder.open(Folder.READ_WRITE);
		}
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.HOUR_OF_DAY, -1); // 过去一小时
		Date sinceDate = cal.getTime();
		log.info("sinceDate : " + sinceDate);
		log.info("开始搜索：" + new Date());
		SearchTerm searchCondition = new ReceivedDateTerm(ComparisonTerm.GE, sinceDate);
		Message[] messages = folder.search(searchCondition);
		log.info("一小时内的邮件数量为：" + messages.length);
		// 进一步在本地过滤匹配标题
		List<Message> recentMessages = new ArrayList<>();
		for (Message message : messages) {
			String subject = message.getSubject();
			if (subject != null && subject.contains(MailConstants.DEAL_SQL_MAIL_TITLE_MATCH)) {
				recentMessages.add(message);
			}
		}
		log.info("Found " + recentMessages.size() + " emails from the last hour with matching titles.");
		return recentMessages;
	}

	public List<MailInfoDTO> parseFileMessage(List<Message> messages) throws Exception {
		if (messages == null || messages.isEmpty()) {
			log.info("没有可读取邮件");
			return null;
		}
		List<MailInfoDTO> rsList = new ArrayList<>();
		// 解析读取到的邮件
		for (Message message : messages) {

			MimeMessage msg = (MimeMessage) message;
			if (redisTemplate.opsForValue()
				.setIfAbsent("mailMessageId:" + msg.getMessageID(), "1", Duration.ofHours(12))) {
				MailInfoDTO mailInfoDTO = new MailInfoDTO();
				ExecSqlDTO execSqlDTO = new ExecSqlDTO();
				execSqlDTO.setGitPathList(new ArrayList<>());
				mailInfoDTO.setExecSqlDTO(execSqlDTO);

				log.info("------------------解析第" + msg.getMessageNumber() + "封邮件-------------------- ");
				String title = MimeUtility.decodeText(msg.getSubject());

				if (redisTemplate.opsForValue().setIfAbsent("mailMessageTitle:" + title, "1", Duration.ofHours(2))) {

					acqDsName(title, execSqlDTO);

					mailInfoDTO.setTitle(title);

					log.info("主题: " + title);
					String from = getFrom(msg, mailInfoDTO);
					log.info("发件人: " + from);
					log.info("邮件id: " + msg.getMessageID() + "-发送时间" + getSentDate(msg.getSentDate(), null));
					log.info("当前时间" + getSentDate(new Date(), null));
					log.info("收件人：" + getReceiveAddress(msg, null, mailInfoDTO));
					log.info("邮件大小：" + msg.getSize() * 1024 + "kb");
					StringBuffer content = new StringBuffer(30);
					getMailTextContent(msg, content);
					log.info("邮件正文：" + content);
					acqSqlInfo(content.toString(), from, execSqlDTO);
					if (CollectionUtils.isNotEmpty(execSqlDTO.getGitPathList())
							|| StringUtils.isNotEmpty(execSqlDTO.getFolder())) {
						// 如果数据库多个，以,分割，那么那么执行多次
						if (mailInfoDTO.getExecSqlDTO().getDsName().indexOf(",") != -1) {
							String[] dsNameArr = mailInfoDTO.getExecSqlDTO().getDsName().split(",");
							for (String dsName : dsNameArr) {
								MailInfoDTO temp = new MailInfoDTO();
								temp.setTitle(mailInfoDTO.getTitle());
								temp.setFrom(mailInfoDTO.getFrom());
								temp.setReceive(mailInfoDTO.getReceive());
								ExecSqlDTO tempSql = new ExecSqlDTO();
								tempSql.setDsName(dsName);
								tempSql.setFolder(mailInfoDTO.getExecSqlDTO().getFolder());
								tempSql.setGitPathList(mailInfoDTO.getExecSqlDTO().getGitPathList());
								temp.setExecSqlDTO(tempSql);
								rsList.add(temp);
							}
						}
						else {
							rsList.add(mailInfoDTO);
						}
					}
					log.info("------------------第" + msg.getMessageNumber() + "封邮件解析结束-------------------- ");
				}
				else {
					log.info("邮箱标题已经处理" + title + "已经处理过了");
				}
			}
			else {
				log.info(msg.getSubject() + "已经处理过了" + msg.getMessageID());
			}

		}
		return rsList;
	}

	/***
	 * @author ccd 提取数据库 , 标题的使用| 分割第二，第五，第六个字符串
	 * @date 2024/12/30 9:33
	 * @version 1.0 库类型枚举:业务库、历史库、审计库、公共库 市场枚举:KE、TZ、BI、SS、ECP
	 * KCB-Sprint1|SIT|自动|脚本|业务库|KE|TRS_CSDCMM_DDL_20250102-01.sql
	 */
	private void acqDsName(String title, ExecSqlDTO execSqlDTO) {
		String[] titleArr = title.split("\\|");
		if (titleArr.length >= 6) {
			String env = titleArr[1];
			String dsType = titleArr[4];
			String tenant = titleArr[5];
			// 拆分 dsType 和 tenant 的值
			String[] dsTypeArr = dsType.split(",");
			String[] tenantArr = tenant.split(",");

			List<String> dsNameList = new ArrayList<>();

			// 生成排列组合
			for (String d : dsTypeArr) {
				for (String t : tenantArr) {
					dsNameList.add(env + "-" + d + "-" + t);
				}
			}

			// 将所有组合用逗号连接
			String dsName = String.join(",", dsNameList);
			log.info("dsName = " + dsName);
			execSqlDTO.setDsName(dsName);
		}
		else {
			execSqlDTO.setDsName(null);
		}
	}

	/***
	 * @description: 从内容忠获取 sql信息
	 * @author ccd
	 * @date 2024/12/30 13:59
	 * @version 1.0
	 */
	private void acqSqlInfo(String content, String from, ExecSqlDTO execSqlDTO) {
		if (StringUtils.isNotEmpty(content)) {
			// 清理 HTML 标签
			String cleanContent = Jsoup.clean(content, Safelist.none());
			// 处理所有可能的换行符
			List<String> lineList = Arrays.asList(cleanContent.split("\\r\\n|\\n|\\r"));

			if (CollectionUtils.isNotEmpty(lineList)) {
				Boolean passFlag = Boolean.FALSE;
				Boolean checkFlag = Boolean.FALSE;

				// 检查邮件来源
				for (String m : onlyCMail.split(",")) {
					if (from.contains(m)) {
						checkFlag = Boolean.TRUE;
						break;
					}
				}

				if (!checkFlag) {
					log.info("---邮件未审批");
					return;
				}

				// 检查审批标志
				for (String s : lineList) {
					log.info("---邮件行内容：" + s);
					if (s.contains(MailConstants.MAIL_PASS_FLAG) && !s.contains(MailConstants.MAIL_PASS_NOT_FLAG)) {
						passFlag = Boolean.TRUE;
						break;
					}
				}

				if (!passFlag) {
					log.info("---邮件审批未通过");
					return;
				}

				log.info("---邮件审批通过，开始处理邮件内容");

				// 正则表达式匹配 .sql 文件和文件夹路径
				Pattern sqlFilePattern = Pattern.compile("http:\\/\\/git\\.murongtech\\.com[^\\s]+?\\.sql");
				// 正则表达式匹配文件夹路径（精确匹配八位日期/文件夹名）
				Pattern folderPattern = Pattern.compile(
						"http:\\/\\/git\\.murongtech\\.com[^\\s]+?tree\\/[^\\/\\s]+\\/(\\d{8}\\/[^\\/\\s]+)(?=[\\s;]|$)");
				// 使用 Set 去重
				Set<String> sqlFileSet = new HashSet<>();
				Set<String> folderSet = new HashSet<>();

				for (String line : lineList) {
					// // 排除隐藏 div 内容
					// if (line.contains("<div") || line.contains("</div>")) {
					// log.info("---跳过隐藏 div 内容: " + line);
					// continue;
					// }

					// 如果该行包含 .sql 文件或文件夹路径，但不是单独一行，则使用正则匹配
					if (line.contains(".sql") || line.contains("tree/")) {
						// 匹配 .sql 文件
						Matcher sqlFileMatcher = sqlFilePattern.matcher(line);
						while (sqlFileMatcher.find()) {
							String sqlFilePath = sqlFileMatcher.group(0);
							log.info("Matched .sql file: " + sqlFilePath);
							sqlFileSet.add(sqlFilePath);
						}

						// 匹配文件夹路径
						Matcher folderMatcher = folderPattern.matcher(line);
						while (folderMatcher.find()) {
							String folderPath = folderMatcher.group(0); // 提取完整文件夹路径
							log.info("Matched folder: " + folderPath);
							folderSet.add(folderPath);
						}
					}
				}
				List<String> processSqlList = new ArrayList<>();
				for (String s : sqlFileSet) {
					if (!s.contains(MailConstants.MAIL_SQL_RB_FLAG)) {
						processSqlList.add(s);
					}
				}
				// 将 Set 转换为 List 并设置到 execSqlDTO
				execSqlDTO.getGitPathList().addAll(processSqlList);
				if (!folderSet.isEmpty()) {
					execSqlDTO.setFolder(folderSet.iterator().next()); // 如果有多个文件夹，只取第一个
				}

				log.info("execSqlDTO = " + JSONObject.toJSONString(execSqlDTO));

				// 处理 BK 的 SQL 文件
				if (CollectionUtils.isNotEmpty(execSqlDTO.getGitPathList())) {
					List<String> sqlList = execSqlDTO.getGitPathList();
					Set<String> set = new HashSet<>(sqlList);
					List<String> list = new ArrayList<>(set);

					List<String> bakExist = new ArrayList<>();
					for (String gitSqlF : list) {
						if (StringUtils.isNotEmpty(gitSqlF)
								&& gitSqlF.toUpperCase().contains(MailConstants.MAIL_SQL_BK_FLAG)) {
							bakExist.add(gitSqlF);
						}
					}

					if (CollectionUtils.isNotEmpty(bakExist)) {
						List<String> rsList = list.stream()
							.filter(info -> !bakExist.contains(info))
							.collect(Collectors.toList());
						for (String s : bakExist) {
							rsList.add(0, s);
						}
						execSqlDTO.setGitPathList(rsList);
					}
					else {
						execSqlDTO.setGitPathList(list);
					}
				}
			}
		}
	}

	/**
	 * 获得邮件发件人
	 * @param msg 邮件内容
	 * @return 姓名 <Email地址>
	 * @throws MessagingException
	 * @throws UnsupportedEncodingException
	 */
	public String getFrom(MimeMessage msg, MailInfoDTO mailInfoDTO)
			throws MessagingException, UnsupportedEncodingException {
		String from = "";
		Address[] froms = msg.getFrom();
		if (froms.length < 1)
			throw new MessagingException("没有发件人!");
		InternetAddress address = (InternetAddress) froms[0];
		String person = address.getPersonal();
		if (person != null) {
			person = MimeUtility.decodeText(person) + " ";
		}
		else {
			person = "";
		}
		from = person + "<" + address.getAddress() + ">";

		mailInfoDTO.setFrom(address.getAddress());

		return from;
	}

	/**
	 * 根据收件人类型，获取邮件收件人、抄送和密送地址。如果收件人类型为空，则获得所有的收件人
	 * <p>
	 * Message.RecipientType.TO 收件人
	 * </p>
	 * <p>
	 * Message.RecipientType.CC 抄送
	 * </p>
	 * <p>
	 * Message.RecipientType.BCC 密送
	 * </p>
	 * @param msg 邮件内容
	 * @param type 收件人类型
	 * @return 收件人1 <邮件地址1>, 收件人2 <邮件地址2>, ...
	 * @throws MessagingException
	 */
	public String getReceiveAddress(MimeMessage msg, Message.RecipientType type, MailInfoDTO mailInfoDTO)
			throws MessagingException {
		StringBuilder receiveAddress = new StringBuilder();
		Address[] addresss;
		if (type == null) {
			addresss = msg.getAllRecipients();
		}
		else {
			addresss = msg.getRecipients(type);
		}

		if (addresss == null || addresss.length < 1) {
			// throw new MessagingException("没有收件人!");
			log.info("没有收件人--" + msg.getSubject() + "--messageId--" + msg.getMessageID());
		}
		for (Address address : addresss) {
			InternetAddress internetAddress = (InternetAddress) address;
			receiveAddress.append(internetAddress.toUnicodeString()).append(",");

			mailInfoDTO.getReceive().add(internetAddress.getAddress());
		}
		receiveAddress.deleteCharAt(receiveAddress.length() - 1); // 删除最后一个逗号
		return receiveAddress.toString();
	}

	/**
	 * 获得邮件文本内容
	 * @param part 邮件体
	 * @param content 存储邮件文本内容的字符串
	 * @throws MessagingException
	 * @throws IOException
	 */
	public void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
		// 如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
		boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
		if (part.isMimeType("text/*") && !isContainTextAttach) {
			content.append(part.getContent().toString());
		}
		else if (part.isMimeType("message/rfc822")) {
			getMailTextContent((Part) part.getContent(), content);
		}
		else if (part.isMimeType("multipart/*")) {
			Multipart multipart = (Multipart) part.getContent();
			int partCount = multipart.getCount();
			for (int i = 0; i < partCount; i++) {
				BodyPart bodyPart = multipart.getBodyPart(i);
				getMailTextContent(bodyPart, content);
			}
		}
	}

	/**
	 * 获得邮件文本内容
	 */
	public void writeMail(MailInfoDTO mailInfoDTO, ExecSqlVO execSqlVO) throws MessagingException, IOException {
		if (sessionWrite == null) {
			getWrSession();
		}
		try {
			// 创建邮件消息对象
			MimeMessage message = new MimeMessage(sessionWrite);
			// 设置发件人
			message.setFrom(new InternetAddress(mailUserName));

			if (StringUtils.isNotEmpty(mailInfoDTO.getFrom())) {
				// 设置收件人
				message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress(mailInfoDTO.getFrom()));
			}

			if (CollectionUtils.isNotEmpty(mailInfoDTO.getReceive())) {
				for (String toOne : mailInfoDTO.getReceive()) {
					// 设置收件人, 不发送给自己
					if (!mailUserName.equals(toOne)) {
						message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress(toOne));
					}
				}
			}
			StringBuffer sbf = new StringBuffer();
			// 说明邮箱发送失败
			if (StringUtils.isNotEmpty(execSqlVO.getLogRs())) {
				// 设置邮件主题
				message.setSubject(mailInfoDTO.getTitle() + "--脚本自动执行失败");
				sbf.append("<b><p><font color='red' size='5'>&nbsp;&nbsp;&nbsp;构建结果：构建失败</font> </p></b>");
			}
			else {
				message.setSubject(mailInfoDTO.getTitle() + "--脚本自动执行成功");
				sbf.append("<b><p><font color='blue' size='5'>&nbsp;&nbsp;&nbsp;构建结果：构建成功</font> </p></b>");
			}
			sbf.append("<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;执行结果信息：" + JSON.toJSONString(execSqlVO) + " </p>");

			ExecSqlDTO execSqlDTO = mailInfoDTO.getExecSqlDTO();
			String jsonString = JSON.toJSONString(execSqlDTO);
			JSONObject jsonObject = JSON.parseObject(jsonString);
			if (jsonObject.containsKey("procTime")) {
				String procTime = jsonObject.getString("procTime");
				jsonObject.put("procTime", procTime + "ms");
			}
			sbf.append("<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sql执行信息：" + jsonObject.toJSONString() + " </p>");
			// 设置邮件正文 <p ><span style=\"font-size:20.0pt\">")
			message.setContent(sbf.toString(), "text/html;charset=utf-8");
			// 发送邮件
			Transport.send(message);
			log.info("邮件发送成功");
		}
		catch (Exception e) {
			log.info("邮件发送失败");
			e.printStackTrace();
		}
	}

	/**
	 * 获得邮件发送时间
	 * @param
	 * @return yyyy年mm月dd日 星期X HH:mm
	 * @throws MessagingException
	 */
	public static String getSentDate(Date receivedDate, String pattern) throws MessagingException {
		if (receivedDate == null)
			return "";

		if (pattern == null || "".equals(pattern))
			pattern = "yyyy年MM月dd日 E HH:mm ";

		return new SimpleDateFormat(pattern).format(receivedDate);
	}

}
